﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Threading;
using System.Windows.Forms;
using Octopus.OctopusService;
using Octopus.OctopusSetupCustomAction;

namespace OctopusConfigurator
{
    public partial class Main : Form
    {
        private string DomainName { get; set; }
        private string DomainControllerName { get; set; }

        private bool needToStopService;
        private bool needToCreateDb;

        private bool credentialsChanged;
        private string newCredentialsDomain;
        private string newCredentialsUsername;
        private string newCredentialsPassword;

        private bool sharedStoragePathChanged;
        private string newSharedStoragePath;

        private bool templatesChanged;
        private bool hypervNodesChanged;

        public Main()
        {
            InitializeComponent();

            this.dataGridViewTemplates.RowsAdded += new DataGridViewRowsAddedEventHandler(dataGridViewTemplates_RowsAdded);
            this.dataGridViewTemplates.RowsRemoved += new DataGridViewRowsRemovedEventHandler(dataGridViewTemplates_RowsRemoved);
            this.dataGridViewTemplates.CellValueChanged += new DataGridViewCellEventHandler(dataGridViewTemplates_CellValueChanged);

            this.checkedListBoxComputers.CheckOnClick = true;
            this.checkedListBoxHyperVNodes.CheckOnClick = true;

            //tabControl1.TabPages.Remove(tabPageHosts);
            //tabControl1.TabPages.Remove(tabPageTemplates);
            //tabControl1.TabPages.Remove(tabPageGeneral);

            this.tabPageSaveChanges.Enter += new EventHandler(tabPageSaveChanges_Enter);

            this.linkLabelMainPerform.Visible = false;

            this.needToStopService = false;
            this.needToCreateDb = false;
            this.credentialsChanged = false;
            this.sharedStoragePathChanged = false;
            this.templatesChanged = false;
            this.hypervNodesChanged = false;

            textBoxMain.Text = "\r\nInitializing...";
            DisableViewing();

            new Thread(new ThreadStart(ExecuteStartupChecks)) { Name = "ExecuteStartupChecks" }.Start();
        }

        private void ExecuteStartupChecks()
        {
            // checks for the Octopus service
            Win32ServiceReturns octopusServiceInfo;
            try
            {
                octopusServiceInfo = Octopus_Win32Service.interrogateService();
            }
            catch
            {
                var foo = new Action(delegate {
                    textBoxMain.Text = "Octopus service not found.";
                    textBoxMain.AppendText("\r\nYou need to run this configurator in the same computer you installed Octopus.");
                });

                if (this.textBoxMain.InvokeRequired)
                    this.Invoke(foo);
                else
                    foo();

                return;
            }

            // checks for the Active Directory information
            try
            {
                //var o1 = Octopus.OctopusLib.Utility.getCustom_WmiObject("\\\\localhost\\root\\directory\\LDAP", "select * from RootDSE");
                //if (o1 != null)
                //    adServer = o1["dnsHostName"].ToString().Split('.')[0];

                var o2 = Octopus.OctopusLib.Utility.get_cimv2_WmiObjects_auth("localhost", "select * from Win32_NTDomain", null, null);
                System.Management.ManagementObject domainObj = null;
                for (int i = 0; i < o2.Length; ++i)
                {
                    if (o2[i]["DomainControllerName"] != null)
                    {
                        domainObj = o2[i];
                        break;
                    }
                }

                if (domainObj == null)
                {
                    var foo = new Action(delegate
                    {
                        textBoxMain.Text = "This computer is not part of a NT Domain.";
                        textBoxMain.AppendText("\r\nOctopus requires the computers to be part of a NT Domain.");
                    });

                    if (this.textBoxMain.InvokeRequired)
                        this.Invoke(foo);
                    else
                        foo();

                    return;
                }
                else
                {
                    DomainControllerName = domainObj["DomainControllerName"].ToString();
                    if (DomainControllerName.StartsWith("\\\\")) DomainControllerName = DomainControllerName.Substring(2);
                    DomainName = domainObj["DomainName"].ToString();
                }

                if (DomainName == null || DomainControllerName == null || DomainName == "" || DomainControllerName == "")
                    throw new Exception();
            }
            catch
            {
                var foo = new Action(delegate
                {
                    textBoxMain.Text = "Unable to retrieve NT Domain information.";
                    textBoxMain.AppendText("\r\nAre you running this application with an administrator account?");
                });

                if (this.textBoxMain.InvokeRequired)
                    this.Invoke(foo);
                else
                    foo();

                return;
            }

            // checks for the SQL service (is it installed?)
            var sqlexpressInstalled = Win32_Service.existsService("MSSQL$SQLEXPRESS");
            if (!sqlexpressInstalled)
            {
                if (this.textBoxMain.InvokeRequired)
                    this.Invoke(new Action(() => EvaluateThingsToDo(false, octopusServiceInfo, false)));
                else
                    EvaluateThingsToDo(false, octopusServiceInfo, false);
                return;
            }

            // checks for the Octopus database in SQL
            bool dbExists = Octopus.OctopusService.Database.Core.checkDatabaseExists();
            if (!dbExists)
            {
                if (this.textBoxMain.InvokeRequired)
                    this.Invoke(new Action(() => EvaluateThingsToDo(sqlexpressInstalled, octopusServiceInfo, false)));
                else
                    EvaluateThingsToDo(sqlexpressInstalled, octopusServiceInfo, false);
                return;
            }

            // checks if the configuration in Octopus is already complete
            if (this.textBoxMain.InvokeRequired)
                this.Invoke(new Action(() => EvaluateThingsToDo(sqlexpressInstalled, octopusServiceInfo, dbExists)));
            else
                EvaluateThingsToDo(sqlexpressInstalled, octopusServiceInfo, dbExists);
            return;
        }

        void EvaluateThingsToDo(bool sqlexpressInstalled, Win32ServiceReturns octopuServiceInfo, bool databaseExists)
        {
            textBoxMain.Text = "Before configuring Octopus, the following actions must be performed:";
            bool haveToPerform = false;

            if (!sqlexpressInstalled)
            {
                textBoxMain.AppendText("\r\n\r\n- You need to install SQL Server Express (SQLEXPRESS instance) before continuing");
                textBoxMain.AppendText("\r\n  you can find it here: http://www.microsoft.com/download/en/details.aspx?id=1695");
                return;
            }

            if (octopuServiceInfo != Win32ServiceReturns.ServiceNotActive)
            {
                textBoxMain.AppendText("\r\n\r\n- Stop the Octopus Service");
                this.needToStopService = true;
                haveToPerform = true;
            }
            if (!databaseExists)
            {
                textBoxMain.AppendText("\r\n\r\n- Create the Octopus database in SQL Server");
                this.needToCreateDb = true;
                haveToPerform = true;
            }

            if (haveToPerform)
            {
                this.linkLabelMainPerform.Visible = true;
                //if (confComplete)
                //    EnableViewing();
            }
            else
            {
                textBoxMain.Text = "\r\nOctopus is ready to be configured";
                EnableEditing();
            }
        }

        private void DisableViewing()
        {
            this.tabControl1.TabPages.Clear();
            this.tabControl1.TabPages.Add(this.tabPageMain);
        }

        private void EnableViewing(bool updateUi = true)
        {
            try
            {
                var cDomain = Octopus.OctopusService.Database.GeneralConfig.get_AdminCredentialsDomain();
                var cUser = Octopus.OctopusService.Database.GeneralConfig.get_AdminCredentialsUsername();
                this.textBoxCredentials.Text = cDomain + "\\" + cUser;
            }
            catch
            {
                this.textBoxCredentials.Text = "";
            }
            credentialsChanged = false;

            try
            {
                this.textBoxSharedStoragePath.Text = Octopus.OctopusService.Database.GeneralConfig.get_SharedStoragePath();
            }
            catch
            {
                this.textBoxSharedStoragePath.Text = "";
            }
            sharedStoragePathChanged = false;

            try
            {
                var templates = Octopus.OctopusService.Database.VMTemplates.getVMTemplates();
                foreach (var template in templates)
                {
                    this.dataGridViewTemplates.Rows.Add(template.ID, template.Name, template.Description, template.VhdPath, template.CpuCoresMin, template.CpuCoresMax, template.MemorySizeMbMin, template.MemorySizeMbMax);
                }
            }
            catch
            {
            }
            templatesChanged = false;

            this.checkedListBoxHyperVNodes.Items.Clear();
            try
            {
                var hyperVNodes = Octopus.OctopusService.Database.HyperVNodes.getHyperVNodes();
                this.checkedListBoxHyperVNodes.Items.AddRange(hyperVNodes);
            }
            catch
            {
            }
            hypervNodesChanged = false;

            if (updateUi)
            {
                this.Invoke(new Action(() =>
                    {
                        this.tabControl1.TabPages.Clear();
                        this.tabControl1.TabPages.Add(this.tabPageMain);
                        this.tabControl1.TabPages.Add(this.tabPageGeneral);
                        this.tabControl1.TabPages.Add(this.tabPageTemplates);
                        this.tabControl1.TabPages.Add(this.tabPageHosts);
                    }));
            }
        }

        private void RefreshListBoxComputers()
        {
            this.checkedListBoxComputers.Items.Clear();
            try
            {
                System.Management.ManagementObject[] objs;
                if (Environment.MachineName.ToUpper() == DomainControllerName.ToUpper())
                {
                    objs = Octopus.OctopusLib.Utility.getCustom_WmiObjects_auth("\\\\localhost\\root\\directory\\LDAP", "SELECT * FROM ads_computer", null, null);
                }
                else
                {
                    string path = String.Format("\\\\{0}\\root\\directory\\LDAP", DomainControllerName);
                    objs = Octopus.OctopusLib.Utility.getCustom_WmiObjects_auth(path, "SELECT * FROM ads_computer", null, null);
                }

                var pcs = (from o in objs
                           let v = o["DS_cn"].ToString()
                           orderby v
                           select v).ToArray();

                this.checkedListBoxComputers.Items.AddRange(pcs);
            }
            catch
            {
            }
        }

        private void EnableEditing(bool updateUi = true)
        {
            EnableViewing(false);

            // checks for the computers which joined the domain
            if (checkedListBoxComputers.Items.Count == 0)
            {
                RefreshListBoxComputers();
            }

            if (updateUi)
            {
                this.Invoke(new Action(() =>
                    {
                        this.tabControl1.TabPages.Clear();
                        this.tabControl1.TabPages.Add(this.tabPageMain);
                        this.tabControl1.TabPages.Add(this.tabPageGeneral);
                        this.tabControl1.TabPages.Add(this.tabPageTemplates);
                        this.tabControl1.TabPages.Add(this.tabPageHosts);
                        this.tabControl1.TabPages.Add(this.tabPageSaveChanges);
                    }));
            }
        }

        /// <summary>
        /// Checks if the specified credentials has Domain Administration rights or not.
        /// Throws an exception if the credentials are invalid.
        /// </summary>
        /// <param name="username">The credentials username</param>
        /// <param name="password">The credentials password</param>
        /// <returns></returns>
        private bool ValidateDomainAdminAccount(string username, string password)
        {
            if (username.Contains("\\"))
                username = username.Split('\\').Last();

            var foo = new Func<System.Security.Principal.IdentityReference[]>(() => System.Security.Principal.WindowsIdentity.GetCurrent().Groups.ToArray());
            var imp = new Impersonation<System.Security.Principal.IdentityReference[]>(this.DomainName, username, password);
            var res = imp.Execute(foo);
            for (int i = 0; i < res.Length; ++i)
            {
                try { res[i] = res[i].Translate(typeof(System.Security.Principal.NTAccount)); }
                catch { }
            }

            var requiredGroup = this.DomainName + "\\Domain Admins";
            var c = (from x in res
                     where x.Value.ToUpper() == requiredGroup.ToUpper()
                     select x).Count();

            return (c > 0);
        }

        private void InvokeIfNeeded(Action foo)
        {
            if (this.textBoxMain.InvokeRequired)
                this.Invoke(foo);
            else
                foo();
        }

        //-------------------------------------------------------------------

        void tabPageSaveChanges_Enter(object sender, EventArgs e)
        {
            linkLabelSaveChanges.Visible = false;
            linkLabelDiscardChanges.Visible = false;
            textBoxSaveChanges.Text = "The following changes will be saved:\r\n";
            bool stuffToSave = false;

            if (this.credentialsChanged)
            {
                textBoxSaveChanges.AppendText("\r\n- New administration credentials");
                this.needToStopService = true;
                stuffToSave = true;
            }
            if (this.sharedStoragePathChanged)
            {
                textBoxSaveChanges.AppendText("\r\n- New shared storage path");
                this.needToCreateDb = true;
                stuffToSave = true;
            }
            if (this.templatesChanged)
            {
                textBoxSaveChanges.AppendText("\r\n- VM Templates changed");
                this.needToCreateDb = true;
                stuffToSave = true;
            }
            if (this.hypervNodesChanged)
            {
                textBoxSaveChanges.AppendText("\r\n- HyperV nodes changed");
                this.needToCreateDb = true;
                stuffToSave = true;
            }

            if (stuffToSave)
            {
                linkLabelSaveChanges.Visible = true;
                linkLabelDiscardChanges.Visible = true;
            }
            else
            {
                textBoxSaveChanges.Text = "Nothing to save.";
            }
        }

        private void linkLabelSelectAllPc_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            for (int i = 0; i < checkedListBoxComputers.Items.Count; ++i)
                checkedListBoxComputers.SetItemChecked(i, true);
        }

        private void linkLabelSelectNonePc_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            for (int i = 0; i < checkedListBoxComputers.Items.Count; ++i)
                checkedListBoxComputers.SetItemChecked(i, false);
        }

        private void linkLabelMainPerform_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            textBoxMain.Text = "\r\nPerforming actions...";
            this.linkLabelMainPerform.Visible = false;
            new Thread(new ThreadStart(PerformActionsForEnableConfiguration)) { Name = "PerformActionsForEnableConfiguration" }.Start();
        }

        private void PerformActionsForEnableConfiguration()
        {
            try
            {
                if (this.needToStopService)
                {
                    Octopus.OctopusSetupCustomAction.Octopus_Win32Service.stopService();
                }
            }
            catch
            {
                InvokeIfNeeded(delegate { textBoxMain.AppendText("\r\n\r\n- Unable to stop the Octopus service"); });
                return;
            }

            try
            {
                if (this.needToCreateDb)
                {
                    Octopus.OctopusService.Database.Core.createDatabaseFile();
                }
            }
            catch
            {
                InvokeIfNeeded(delegate { textBoxMain.AppendText("\r\n\r\n- Unable to create the Octopus database"); });
                return;
            }

            //-----

            InvokeIfNeeded(delegate
            {
                textBoxMain.AppendText(" done.\r\n\r\nYou can configure Octopus now.");
                this.linkLabelMainPerform.Visible = false;
                EnableEditing();
            });
        }

        private void linkLabelCredentials_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            var cred = new MyCredentials(this.DomainName + "\\");
            bool ret = cred.Show();
            if (ret && cred.UserName != null && cred.UserName.Trim() != "" && cred.Password != null && cred.Password.Trim() != "")
            {
                var username = cred.UserName;
                if (username.Contains("\\"))
                {
                    if (username.Split('\\')[0].ToUpper() != this.DomainName.ToUpper())
                    {
                        MessageBox.Show("You must specify a domain account", "Domain Admin credentials");
                        return;
                    }
                    username = username.Split('\\')[1].Trim();
                }

                bool validAdmin;
                try
                {
                    validAdmin = ValidateDomainAdminAccount(username, cred.Password);
                }
                catch (Exception ex)
                {
                    MessageBox.Show(ex.Message, "Domain Admin credentials");
                    return;
                }

                if (validAdmin == false)
                {
                    MessageBox.Show("The credentials you specified are not in the Domain Admins group", "Domain Admin credentials");
                    return;
                }
                else
                {
                    this.credentialsChanged = true;
                    this.newCredentialsDomain = this.DomainName;
                    this.newCredentialsUsername = username;
                    this.newCredentialsPassword = cred.Password;

                    this.textBoxCredentials.Text = newCredentialsUsername;
                }
            }
        }

        private void linkLabelSharedStoragePath_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            var d = new Microsoft.WindowsAPICodePack.Dialogs.CommonOpenFileDialog();
            d.IsFolderPicker = true;
            var res = d.ShowDialog();
            if (res == Microsoft.WindowsAPICodePack.Dialogs.CommonFileDialogResult.Ok)
            {
                this.sharedStoragePathChanged = true;
                this.newSharedStoragePath = d.FileName;

                this.textBoxSharedStoragePath.Text = newSharedStoragePath;
            }
        }

        private void linkLabelNewTemplate_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            var openfilediag = new OpenFileDialog();
            openfilediag.DefaultExt = "vhd";
            openfilediag.Filter = "Virtual Hard Disks (*.vhd)|*.vhd";
            openfilediag.CheckFileExists = true;
            var res = openfilediag.ShowDialog();
            if (res == System.Windows.Forms.DialogResult.OK)
            {
                this.dataGridViewTemplates.Rows.Add(-1, "NewRow", "Specify a description here", openfilediag.FileName, 1, 4, 512, 4096);
            }
        }

        private void dataGridViewTemplates_RowsAdded(object sender, DataGridViewRowsAddedEventArgs e)
        {
            this.templatesChanged = true;
        }
        private void dataGridViewTemplates_RowsRemoved(object sender, DataGridViewRowsRemovedEventArgs e)
        {
            this.templatesChanged = true;
        }
        private void dataGridViewTemplates_CellValueChanged(object sender, DataGridViewCellEventArgs e)
        {
            this.templatesChanged = true;
        }

        private void buttonUndoTableTemplate_Click(object sender, EventArgs e)
        {
            this.dataGridViewTemplates.Rows.Clear();
            var templates = Octopus.OctopusService.Database.VMTemplates.getVMTemplates();
            foreach (var template in templates)
            {
                this.dataGridViewTemplates.Rows.Add(template.ID, template.Name, template.Description, template.VhdPath, template.CpuCoresMin, template.CpuCoresMax, template.MemorySizeMbMin, template.MemorySizeMbMax);
            }
            this.templatesChanged = false;
        }

        private bool ListBoxHyperVNodesAlreadyHas(string host)
        {
            foreach (var x in this.checkedListBoxHyperVNodes.Items)
            {
                var vnode = x.ToString().ToUpper().Trim().Split(' ')[0];
                if (vnode == host.ToUpper())
                    return true;
            }
            return false;
        }

        private void buttonAddHyperVNode_Click(object sender, EventArgs e)
        {
            var items = this.checkedListBoxComputers.CheckedItems;
            if (items.Count == 0)
                return;

            foreach (var item in items)
            {
                var hostName = (item as string).Trim();
                if (!ListBoxHyperVNodesAlreadyHas(hostName))
                {
                    hypervNodesChanged = true;
                    hostName = hostName + " (*)";
                    this.checkedListBoxHyperVNodes.Items.Add(hostName);
                }
            }

            for (int i = 0; i < checkedListBoxComputers.Items.Count; ++i)
                checkedListBoxComputers.SetItemChecked(i, false);
        }

        private void buttonRemoveHyperVNodes_Click(object sender, EventArgs e)
        {
            var items = this.checkedListBoxHyperVNodes.CheckedItems;
            if (items.Count == 0)
                return;

            object[] oitems = new object[items.Count];
            items.CopyTo(oitems, 0);

            hypervNodesChanged = true;

            foreach (var item in oitems)
                this.checkedListBoxHyperVNodes.Items.Remove(item);
        }

        private void buttonUndoHyperVNodesChanges_Click(object sender, EventArgs e)
        {
            this.checkedListBoxHyperVNodes.Items.Clear();
            var hyperVNodes = Octopus.OctopusService.Database.HyperVNodes.getHyperVNodes();
            this.checkedListBoxHyperVNodes.Items.AddRange(hyperVNodes);
            this.hypervNodesChanged = false;
        }

        private void linkLabelMainStartService_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            if (Octopus_Win32Service.existsService())
            {
                var status = Octopus_Win32Service.interrogateService();
                if (status == Win32ServiceReturns.ServiceNotActive)
                {
                    try
                    {
                        Octopus_Win32Service.startService();
                    }
                    catch (Exception exc)
                    {
                        MessageBox.Show(exc.Message, "Starting Octopus service", MessageBoxButtons.OK);
                    }
                }
                else
                {
                    MessageBox.Show("Error: service not stopped", "Starting Octopus service", MessageBoxButtons.OK);
                }
            }
            else
            {
                MessageBox.Show("Error: service not found", "Starting Octopus service", MessageBoxButtons.OK);
            }
        }

        private void linkLabelDiscardChanges_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            EnableEditing();
            linkLabelSaveChanges.Visible = false;
            linkLabelDiscardChanges.Visible = false;
            textBoxSaveChanges.Text = "Nothing to save.";
        }

        private void linkLabelSaveChanges_LinkClicked(object sender, LinkLabelLinkClickedEventArgs e)
        {
            linkLabelSaveChanges.Visible = false;
            linkLabelDiscardChanges.Visible = false;
            textBoxSaveChanges.AppendText("\r\n\r\nSaving...");
            this.tabControl1.TabPages.Remove(this.tabPageGeneral);
            this.tabControl1.TabPages.Remove(this.tabPageTemplates);
            this.tabControl1.TabPages.Remove(this.tabPageHosts);
            this.tabPageSaveChanges.Enter -= new EventHandler(tabPageSaveChanges_Enter);

            new Thread(new ThreadStart(StartSaving)) { Name = "Saving thread" }.Start();
        }

        private void StartSaving()
        {
            bool serviceStopped = false;

            Win32ServiceReturns octopusServiceInfo;
            try
            {
                octopusServiceInfo = Octopus_Win32Service.interrogateService();
            }
            catch
            {
                InvokeIfNeeded(delegate
                {
                    textBoxSaveChanges.AppendText(" error!\r\n");
                    textBoxSaveChanges.AppendText("Octopus service not found. Are you running this configurator on the same machine you installed octopus?");
                });
                return;
            }

            if (octopusServiceInfo != Win32ServiceReturns.ServiceNotActive)
            {
                try
                {
                    Octopus_Win32Service.stopService();
                    serviceStopped = true;
                }
                catch
                {
                    InvokeIfNeeded(delegate
                    {
                        textBoxSaveChanges.AppendText(" error!\r\n");
                        textBoxSaveChanges.AppendText("Unable to stop the Octopus scheduler service.");
                    });
                    return;
                }
            }

            //-----

            InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("...\r\n"); });

            bool credentialChangedSuccesfully = false;
            if (this.credentialsChanged)
            {
                try
                {
                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("Storing Admin credentials... "); });

                    try
                    {
                        Octopus.OctopusService.Database.GeneralConfig.delete_AdminCredentialsDomain();
                        Octopus.OctopusService.Database.GeneralConfig.delete_AdminCredentialsUsername();
                        Octopus.OctopusService.Database.GeneralConfig.delete_AdminCredentialsPassword();
                    }
                    catch { }

                    Octopus.OctopusService.Database.GeneralConfig.set_AdminCredentialsDomain(this.newCredentialsDomain);
                    Octopus.OctopusService.Database.GeneralConfig.set_AdminCredentialsUsername(this.newCredentialsUsername);
                    Octopus.OctopusService.Database.GeneralConfig.set_AdminCredentialsPassword(this.newCredentialsPassword);
                    credentialChangedSuccesfully = true;
                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("done.\r\n"); });
                }
                catch (Exception exc)
                {
                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("failed: " + exc.Message + "\r\n"); });
                }
            }

            bool sharedStoragePathChangedSuccesfully = false;
            if (this.sharedStoragePathChanged)
            {
                try
                {
                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("Storing the shared storage path... "); });

                    try
                    {
                        Octopus.OctopusService.Database.GeneralConfig.delete_SharedStoragePath();
                    }
                    catch { }

                    Octopus.OctopusService.Database.GeneralConfig.set_SharedStoragePath(this.newSharedStoragePath);
                    sharedStoragePathChangedSuccesfully = true;
                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("done.\r\n"); });
                }
                catch (Exception exc)
                {
                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("failed: " + exc.Message + "\r\n"); });
                }
            }

            bool templatesChangedSuccesfully = false;
            if (this.templatesChanged)
            {
                try
                {
                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("Updating the VM templates... "); });

                    var templatesInDb = Octopus.OctopusService.Database.VMTemplates.getVMTemplates().Select(x => x.ID).ToList();
                    var templatesInGrid = new DataGridViewRow[this.dataGridViewTemplates.Rows.Count];
                    this.dataGridViewTemplates.Rows.CopyTo(templatesInGrid, 0);

                    foreach (var row in templatesInGrid)
                    {
                        int rowId = (row.Cells[0].Value is int) ? ((int)row.Cells[0].Value) : (int.Parse(row.Cells[0].Value.ToString()));
                        if (rowId == -1)
                        {
                            string name = row.Cells[1].Value.ToString();
                            string description = row.Cells[2].Value.ToString();
                            string vhdPath = row.Cells[3].Value.ToString();
                            ushort minCores = (row.Cells[4].Value is int) ? ((ushort)(int)row.Cells[4].Value) : (ushort.Parse(row.Cells[4].Value.ToString()));
                            ushort maxCores = (row.Cells[5].Value is int) ? ((ushort)(int)row.Cells[5].Value) : (ushort.Parse(row.Cells[5].Value.ToString()));
                            int minRam = (row.Cells[6].Value is int) ? ((int)row.Cells[6].Value) : (int.Parse(row.Cells[6].Value.ToString()));
                            int maxRam = (row.Cells[7].Value is int) ? ((int)row.Cells[7].Value) : (int.Parse(row.Cells[7].Value.ToString()));

                            Octopus.OctopusService.Database.VMTemplates.insertNewVMTemplate(name, description, minCores, maxCores, minRam, maxRam, "New Virtual Network", vhdPath, -1, false, null);
                        }
                        else
                        {
                            templatesInDb.Remove(rowId);
                        }
                    }

                    // TODO: implementare la remove
                    //foreach (var t in templatesInDb)
                    //{
                    //    Database.VMTemplates.
                    //}

                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("done.\r\n"); });
                }
                catch (Exception exc)
                {
                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("failed: " + exc.Message + "\r\n"); });
                }
            }

            bool hypervNodesChangedSuccesfully = false;
            if (this.hypervNodesChanged)
            {
                try
                {
                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("Updating HyperV nodes... "); });
                    foreach (var n in this.checkedListBoxHyperVNodes.Items)
                    {
                        if (n is Octopus.OctopusLib.HyperVNode)
                        {
                            //var hvNode = (Octopus.OctopusLib.HyperVNode)n;
                        }
                        else
                        {
                            var host = ((string)n).Split(' ')[0];
                            var server = new Microsoft.FSharp.Core.FSharpOption<string>(host);

                            try
                            {
                                var nicSwitch = HyperF.HyperF.get_AllVMSwitch(server).First();
                                var hvNode = new Octopus.OctopusLib.HyperVNode(-1, host, "0.0.0.0", nicSwitch.Guid, 0, 0, false, 0f, 0L, System.DateTime.Now);
                                Octopus.OctopusService.Database.HyperVNodes.insertHyperVNode(hvNode);
                            }
                            catch (Exception exc)
                            {
                                InvokeIfNeeded(delegate
                                    {
                                        textBoxSaveChanges.AppendText("Unable to add the Hyper-V server: " + host + ".\r\n");
                                        textBoxSaveChanges.AppendText("   : " + exc.Message);
                                    });
                            }
                        }
                    }

                    //TODO: implementare la remove

                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("done.\r\n"); });
                }
                catch (Exception exc)
                {
                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("failed: " + exc.Message + "\r\n"); });
                }
            }

            if (serviceStopped)
            {
                try
                {
                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("Restarting the Octopus scheduler service... "); });
                    Octopus_Win32Service.stopService();
                    serviceStopped = true;
                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("done.\r\n"); });
                }
                catch (Exception exc)
                {
                    InvokeIfNeeded(delegate { textBoxSaveChanges.AppendText("failed: " + exc.Message + "\r\n"); });
                }
            }

            //----------------

            if (credentialChangedSuccesfully && sharedStoragePathChangedSuccesfully && templatesChangedSuccesfully && hypervNodesChangedSuccesfully)
            {
                InvokeIfNeeded(delegate
                {
                    textBoxSaveChanges.AppendText("Done saving!\r\n");
                });
            }
        }
    }
}
